﻿using System.ComponentModel;
using System.Threading;

using log4net;

namespace StreakyDownloader.Model
{
   /// <summary>
   /// Provides the thread of execution that downloads each of the files.
   /// </summary>
   public class DownloaderThread
   {
      private ILog logger = LogManager.GetLogger(typeof(DownloaderThread));

      protected DownloadQueue m_fileDownloadQueue;
      protected FileDownloader m_fileDownloader;
      
      protected AutoResetEvent m_fileDownloadComplete;
      protected AutoResetEvent m_allFilesCompleteEvent;

      protected bool m_allfilesDownloaded;
      protected bool m_userCancelled;

      #region Properties
      public bool AllFilesDownloaded
      {
         get { return m_allfilesDownloaded; }
      }
      #endregion

      /// <summary>
      /// Create the downloader thread with the specified queue of file ids, and the specified
      /// FileDownload to handle the downloading of each of the files.
      /// </summary>
      /// <param name="fileDLQueue">List of the files to download.</param>
      /// <param name="fileDownloader">Local filename to download to.</param>
      public DownloaderThread(DownloadQueue fileDLQueue, FileDownloader fileDownloader)
      {
         m_fileDownloadQueue = fileDLQueue;
         m_fileDownloader = fileDownloader;

         m_fileDownloadComplete = new AutoResetEvent(false);
         m_allFilesCompleteEvent = new AutoResetEvent(false);

         m_allfilesDownloaded = false;

         m_fileDownloader.m_fileComplete += new FileCompleteHandler(FileDownloadComplete);
      }

      /// <summary>
      /// Kicks of the asynchronous download of all of the files in the list (download queue).
      /// This function returns immediately, however the download is not complete until the 
      /// </summary>
      public void DownloadFiles()
      {
         m_allfilesDownloaded = false;

         BackgroundWorker downloadThread = new BackgroundWorker();
         downloadThread.DoWork += new DoWorkEventHandler(DownloadThreadWorker);

         downloadThread.RunWorkerAsync();
      }

      /// <summary>
      /// The worker thread to download all of the files for the 
      /// </summary>
      /// <param name="sender">Object that started this thread worker.</param>
      /// <param name="e">Arguments to pass to the new thread.</param>
      protected void DownloadThreadWorker(object sender, DoWorkEventArgs e)
      {
         try
         {
            if (!e.Cancel)
            {
               foreach (DownloadFile file in m_fileDownloadQueue)
               {
                  if (!m_userCancelled)
                  {
                     StartDownloadingFile(file);

                     // wait until download is done before going on to the next
                     // file to download.
                     m_fileDownloadComplete.WaitOne();
                  }
                  else
                  {
                     file.UserCancelled = true;
                  }
               }
            }
         }
         finally
         {
            m_allFilesCompleteEvent.Set();

            // flag a booling for the OUTSIDE to see if all of the downloading 
            // has finished
            m_allfilesDownloaded = true;
         }
      }

      /// <summary>
      /// Interrupt the file download process.
      /// </summary>
      public void CancelDownloads()
      {
         // cancel the current file being downloaded
         // the current file will still call its Completed handler with the 
         // arguments indicating the download was cancelled.
         m_fileDownloader.CancelDownload();

         m_userCancelled = true;
      }

      /// <summary>
      /// Begin downloading the specified file asynchronously.  
      /// When the file download ends, the Complete event handler is called.
      /// </summary>
      /// <param name="id">The id of the file to begin downloading.</param>
      protected void StartDownloadingFile(DownloadFile file)
      {
         m_fileDownloader.Download(file);
      }

      /// <summary>
      /// Handler called when a file downloaded is ended, either
      /// completed successfully or an error occurred.
      /// </summary>
      /// <param name="result">Result of the file being downloaded.</param>
      protected void FileDownloadComplete(EDownloadResult result)
      {
         if (EDownloadResult.SUCCESS != result)
         {
            logger.Error("Error Occured while downloading " + m_fileDownloader.CurrentFile.FileID);
         }

         // signal we are ready to move onto the next file.
         m_fileDownloadComplete.Set();
      }
   }
}
